package com.sofar.jwt.utils;

import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.ser.FilterProvider;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

import java.io.IOException;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;

/**
 * @author shiguorang
 * @version 1.0.0
 * @ClassName JsonUtils
 * @Description TODO
 * @createTime 2022年08月15日 10:07:00
 */
public class JsonUtils {

    private JsonUtils() {
    }

    public static final ObjectMapper OBJECT_MAPPER = createObjectMapper();

    private static final ObjectMapper IGNORE_OBJECT_MAPPER = createIgnoreObjectMapper();

    private static ObjectMapper createIgnoreObjectMapper() {
        ObjectMapper objectMapper = createObjectMapper();
        objectMapper.addMixIn(Object.class, DynamicMixIn.class);
        return objectMapper;
    }

    public static ObjectMapper createObjectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
        objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true);
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        objectMapper.registerModule(new JavaTimeModule());
        return objectMapper;
    }

    public static String object2Json(Object o) {
        StringWriter sw = new StringWriter();
        JsonGenerator gen = null;
        try {
            gen = new JsonFactory().createGenerator(sw);
            OBJECT_MAPPER.writeValue(gen, o);
        } catch (IOException e) {
            throw new RuntimeException("Cannot serialize object as JSON", e);
        } finally {
            if (null != gen) {
                try {
                    gen.close();
                } catch (IOException e) {
                    throw new RuntimeException("Cannot serialize object as JSON", e);
                }
            }
        }
        return sw.toString();
    }

    public static String object2Json(Object o, String... ignoreFiledNames) {
        try {
            SimpleBeanPropertyFilter theFilter = SimpleBeanPropertyFilter.serializeAllExcept(ignoreFiledNames);
            FilterProvider filters = new SimpleFilterProvider().addFilter("dynamicFilter", theFilter);
            return IGNORE_OBJECT_MAPPER.writer(filters).writeValueAsString(o);
        } catch (IOException e) {
            throw new RuntimeException("Cannot serialize object as JSON", e);
        }
    }

    @JsonFilter("dynamicFilter")
    private static class DynamicMixIn {

    }

    public static Map<String, Object> object2Map(Object o) {
        return OBJECT_MAPPER.convertValue(o, Map.class);
    }

    public static <T> T json2Object(String json, Class<T> clazz) {
        try {
            return OBJECT_MAPPER.readValue(json, clazz);
        } catch (IOException e) {
            throw new RuntimeException("Exception converting JSON to object, json: " + json, e);
        }
    }

    /**
     * 把source转为target
     *
     * @param source source
     * @param target target
     * @param <T>    返回值类型
     * @return 返回值
     * @throws Exception newInstance可能会抛出的异常
     */
    public static <T> T mapToObject(Map source, Class<T> target) {
        Field[] fields = target.getDeclaredFields();
        try {
            T o = target.newInstance();
            for (Field field : fields) {
                Object val;
                if ((val = source.get(field.getName())) != null) {
                    field.setAccessible(true);
                    field.set(o, val);
                }
            }
            return o;
        } catch (Exception e) {
            throw new RuntimeException("Exception converting JSON to object, json: " + source, e);
        }
    }

    public static <T> List<T> json2List(String json, Class<T> clazz) throws IOException {
        JavaType type = OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, clazz);

        return OBJECT_MAPPER.readValue(json, type);
    }


    public static <T> T[] json2Array(String json, Class<T[]> clazz) throws IOException {
        return OBJECT_MAPPER.readValue(json, clazz);

    }

    public static <T> T node2Object(JsonNode jsonNode, Class<T> clazz) {
        try {
            return OBJECT_MAPPER.treeToValue(jsonNode, clazz);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("Exception converting JSON to object, json: " + jsonNode.toString(), e);
        }
    }

    public static JsonNode object2Node(Object o) {
        try {
            if (o == null) {
                return OBJECT_MAPPER.createObjectNode();
            } else {
                return OBJECT_MAPPER.convertValue(o, JsonNode.class);
            }
        } catch (Exception e) {
            throw new RuntimeException("Cannot serialize object as JSON", e);
        }
    }

    public static <T> T json2GenericObject(String json, TypeReference<T> tr) {

        if (json == null || "".equals(json)) {
            throw new RuntimeException("Exception converting JSON to object, json: " + json);
        } else {
            try {
                return (T) OBJECT_MAPPER.readValue(json, tr);
            } catch (Exception e) {
                throw new RuntimeException("Exception converting JSON to object, json: " + json, e);
            }
        }
    }

}
